using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;
using System.Linq;
using Ro.Norc.ServiceStackClient;

namespace Ro.Norc.ServiceStackClientCustomEncoderSample
{
	public class ServiceEncoderForm : ServiceEncoderBase
	{
		public ServiceEncoderForm()
		{
			_contentType = "application/x-www-form-urlencoded";
			_endpointPart = null;
			_allowedTypes = null;
		}
		
		private List<PropertyInfo> GetProperties(object obj)
		{
			return GetProperties(obj.GetType());
		}
		
		private List<PropertyInfo> GetProperties(Type t)
		{
			PropertyInfo[] allProps = null;
			List<PropertyInfo> filteredProps = null;
			allProps = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
			if (allProps == null) {
				return null;
			}
			filteredProps = new List<PropertyInfo>(allProps.Length);
			foreach (PropertyInfo p in allProps) {
				DataMemberAttribute[] attr = p.GetCustomAttributes(typeof(DataMemberAttribute), true) 
					as DataMemberAttribute[];
				if (attr == null || attr.Length == 0) {
					continue;
				}
				filteredProps.Add(p);
			}
			return filteredProps;
		}
		
		public override string Encode (object obj)
		{
			List<string> values = new List<string>();
			List<PropertyInfo> props = null;
			object val = null;
			if (obj == null) {
				return string.Empty;
			}
			props = GetProperties(obj);
			if (props == null || props.Count == 0) {
				return string.Empty;
			}
			foreach (PropertyInfo p in props) {
				val = p.GetValue(obj, null);
				if (val != null) {
					values.Add(p.Name + "=" + val.ToString());
				}
			}
			
			return string.Join<string>("&", values);
		}
		
		public override object Decode (string repr, Type t)
		{
			object obj = null;
			List<PropertyInfo> props = null;
			string[] values = null;
			if (t == null) {
				throw new ArgumentNullException("t");
			}
			if (repr == null) {
				return null;
			}
			obj = Activator.CreateInstance(t);
			props = GetProperties(t);
			if (props == null || props.Count == 0) {
				return null;
			}
			repr = repr.TrimStart(new char[] {
				'?'
			});
			values = repr.Split(new char[] {
				'&'
			});
			foreach (string pair in values) {
				string[] kv = pair.Split(new char[] {
					'='
				}, 2);
				string key = kv[0];
				string val = kv.Length == 2 ? kv[1] : null;
				if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(val)) {
					continue;
				}
				PropertyInfo prop = props.Where(p => p.Name == key).FirstOrDefault();
				if (prop == null) {
					continue;
				}
				prop.SetValue(obj, Convert.ChangeType(val, prop.PropertyType), null);
			}
			
			return obj;
		}
	}
}